﻿/// <reference path="//Microsoft.WinJS.1.0/js/base.js" />
/// <reference path="//Microsoft.WinJS.1.0/js/ui.js" />
/// <reference path="../../lib/underscore.js" />
/// <reference path="FolioFixer_Calculators.js" />
/// <reference path="../Structures/FolioFixer_DataTypes.js" />
(function () {
    
    

    var PortfolioLocalAdapter = WinJS.Class.define(
        function (key, error) {
            this.key = key;
            this._error = error;
        },
        {
            /* IListDataAdapter Interface Methods */

            getCount: function () {
                var that = this;

                var count = 0;
                var item = that._getValue();
                if (item && item.length) {
                    count = item.length;
                }

                return new WinJS.Promise.wrap(count);
            },

            itemsFromIndex: function (requestIndex, countBefore, countAfter) {
                var that = this;
                
                var array = that._getValue();
                var output = null;
                var IFetchResult = null;

                var startIndex = requestIndex - countBefore;
                var endIndex = requestIndex + countAfter + 1;

                return new WinJS.Promise(
                    function (complete, error) {
                        that.getCount().then(
                            function (count) {
                                if (requestIndex >= count) {
                                    error(new WinJS.ErrorFromName(WinJS.UI.FetchError.doesNotExist));
                                    return;
                                }

                                if (endIndex > count) {
                                    endIndex = count;
                                }

                                if (startIndex < 0) {
                                    startIndex = 0;
                                }

                                output = array.slice(startIndex, endIndex);
                                var items = [];

                                output.forEach(function (item, index) {
                                    items.push({
                                        key: index.toString(),
                                        data: item
                                    });
                                });

                                IFetchResult = {
                                    items: items, // Gets or sets the items returned by the fetch operation.
                                    absoluteIndex: requestIndex,
                                    atStart: startIndex == 0,
                                    atEnd: endIndex == count,
                                    offset: requestIndex - startIndex,
                                    totalCount: count
                                };

                                complete(IFetchResult);
                            }
                        );
                    }
                );
            },

            insertAtEnd: function (unused, data) {
                var that = this;
                
                var current = that._getValue();
                
                var index = current.push(data) - 1;
                data.index = index;

                that._setValue(current);

                var newItem = {
                    index: index,
                    key: index,
                    data: data
                }

                if(that._notificationHandler)
                    that._notificationHandler.inserted(newItem, index-1, null, index);
                
                return WinJS.Promise.wrap(index, data);
            },

            setNotificationHandler: function (notificationHandler) {
                this._notificationHandler = notificationHandler;
            },

            /* IndexedDbDataSource method */

            removeInternal: function (key) {
                var that = this;

                var value = that._getValue();
                var newValue = _.without(value, value[key]);
                that._setValue(newValue);

                that._notificationHandler.removed(key);
                return new WinJS.Promise.wrap();
            },

            nuke: function () {
                var that = this;

                that._setValue([]);

                that._notificationHandler.reload();

                return new WinJS.Promise.wrap();
            },

            getFromKey: function (key) {
                var that = this;

                var array = that._getValue();
                var item = array[key];

                return new WinJS.Promise.wrap(item);
            },

            put: function(data) {
                var that = this;

                var items = that._getValue();

                var putIndex = -1;

                for (var i = 0; i < items.length; i++) {
                    var item = items[i];
                    if (item.index == data.index) {
                        putIndex = i;
                        break;
                    }
                }

                if (putIndex != -1) {
                    items[putIndex] = data;
                    that._setValue(items);

                    var newItem = {
                        index: putIndex,
                        data: data
                    }

                    that._notificationHandler.changed(newItem);

                    return WinJS.Promise.wrap();
                } else {
                    return that.insertAtEnd(null, data);
                }

                
            },

            _getValue: function () {
                var obj = JSON.parse(localStorage.getItem(this.key));
                if (obj == null) obj = [];
                if (!_.isArray(obj)) {
                    obj = [obj];
                }
                return obj;
            },

            _setValue: function (value) {
                var text = JSON.stringify(value);
                localStorage.setItem(this.key, text);
            }
        }
    );

    var PortfolioLocalDataAdapter = new WinJS.Class.derive(
        PortfolioLocalAdapter,
        function () {
            this.key = "portfolios";
        },
        {
            _getValue: function () {
                var obj = JSON.parse(localStorage.getItem(this.key));
                if (obj == null) obj = [];
                if (!_.isArray(obj)) {
                    obj = [obj];
                }

                obj.forEach(function (portfolio) {
                    portfolio.date = new FolioFixer.DataTypes.Date(portfolio.date.year, portfolio.date.month, portfolio.date.date);
                });

                return obj;
            }
        }
    );

    var PortfolioLocalDataSource = new WinJS.Class.derive(
        WinJS.UI.VirtualizedDataSource,
        function (error) {

            this._adapter = new PortfolioLocalDataAdapter();

            var ports = localStorage.getItem("portfolios");

            if (!ports || (_.isArray(ports) && ports.length == 0)) {
                this._adapter.put(new FolioFixer.DataTypes.PortfolioInfo("Big Four", new FolioFixer.DataTypes.Date(2008, 8, 10), ["MSFT", "AAPL", "GOOG", "FB"]));
            }

            this._baseDataSourceConstructor(this._adapter);
        },
        {
            nuke: function () {
                return this._adapter.nuke();
            },

            remove: function (key) {
                return this._adapter.removeInternal(key);
            },

            add: function (obj) {
                return this._adapter.insertAtEnd(null, obj);
            },

            getFromKey: function (key) {
                return this._adapter.getFromKey(key);
            },

            put: function (data) {
                return this._adapter.put(data);
            }
        }
    );

    var AssetDataAdapter = new WinJS.Class.define(
        function (portfolioCalculator, dbVersion) {
            this._portfolioCalculator = portfolioCalculator;
            this._portfolio = portfolioCalculator.portfolio;
            this._portfolioSource = new PortfolioLocalDataSource();

            //this._portfolioSource = new PortfolioLocalDataSource();

            //this._portfolioSource.put(portfolioCalculator.portfolio);
        },
        {
            /* IListDataAdapter Interface Methods */

            getCount: function () {
                var that = this;
                return new WinJS.Promise(function(complete) {
                    that._getPortfolio().done(
                        function (item) {
                            complete(item.assets.length);
                        });
                });
            },

            itemsFromIndex: function (requestIndex, countBefore, countAfter) {
                var that = this;
                
                return new WinJS.Promise(function (complete, error) {
                    that._getPortfolioCalculator().then(
                        function (item) {
                            
                            var portfolio = item.portfolio;
                            var length = portfolio.assets.length;
                            if (requestIndex >= length || requestIndex < 0) {
                                error(requestIndex);
                            }

                            var first = Math.max(requestIndex - countBefore, 0);

                            var last = Math.min(requestIndex + countAfter + 1, length);

                            var items = [];

                            item.getAssetCalculators().done(
                                function (calcs) {
                                    var pAllocations = [];
                                    var allocations = [];

                                    calcs.forEach(
                                        function (calc) {
                                            var promise = calc.getAnnualizedStandardDeviationWeight().done(
                                                function (weight) {
                                                    allocations.push({ weight: Math.round(weight*10000)/100 + "%", symbol: calc.asset.symbol });
                                                }
                                            );
                                            pAllocations.push(promise);
                                        }
                                    );

                                    WinJS.Promise.join(pAllocations).done(
                                        function () {
                                            portfolio.assets.slice(first, last).forEach(
                                                function (value, index) {
                                                    var data = _.find(allocations, function (allocation) {
                                                        return allocation.symbol == value;
                                                    });

                                                    items.push({
                                                        key: (first + index).toString(),
                                                        data: {
                                                            symbol: data.symbol,
                                                            allocation: data.weight
                                                        }
                                                    });
                                                }
                                            );

                                            complete(
                                                {
                                                    items: items,
                                                    offset: requestIndex - first
                                                }
                                            );
                                        }
                                    );
                                }
                            );
                        }
                    );
                });
            },

            insertAtEnd: function (unused, data) {
                var that = this;
                return that._getPortfolio().then(
                        function (request) {
                            var portfolio = request;
                            portfolio.assets.push(data);
                            return that._updatePortfolio(portfolio);
                        });
            },

            nuke: function () {
                var that = this;
                return that._getPortfolio().then(
                    function (portfolio) {
                        portfolio.assets = [];
                        return that._portfolioSource.put(portfolio);
                    });
            },

            removeInternal: function(key) {
                var that = this;
                return that._getPortfolio().then(
                    function (portfolio) {
                        var idx = portfolio.assets.indexOf(key);
                        if (idx != -1) portfolio.assets.splice(idx, 1);
                        return that._portfolioSource.put(portfolio);
                    }
                );
            },

            setNotificationHandler: function (notificationHandler) {
                this._notificationHandler = notificationHandler;
            },

            refresh: function() {
                this._notificationHandler.reload();
            },

            setDate: function (date) {
                var that = this;

                return that._getPortfolio().then(
                    function (portfolio) {
                        portfolio.date = date;
                        return that._updatePortfolio(portfolio);
                    }
                );
            },

            _updatePortfolio: function (portfolio) {
                var that = this;

                that._portfolioCalculator.portfolio = portfolio;

                that._portfolioCalculator.refresh();

                that._portfolioSource.put(portfolio);

                that.refresh();

                return that._portfolioSource.put(portfolio);
            },

            /* Private Methods */
            _getPortfolioCalculator: function() {
                return new WinJS.Promise.wrap(this._portfolioCalculator);
            },

            _getPortfolio: function () {
                var that = this;
                return new WinJS.Promise.wrap(that._portfolioCalculator.portfolio);
            }
        }
    );

    var AssetDataSource = new WinJS.Class.derive(
        WinJS.UI.VirtualizedDataSource,
        function (portfolioCalculator, dbVersion) {

            this._adapter = new AssetDataAdapter(portfolioCalculator, dbVersion);
            this.portfolioCalculator = portfolioCalculator;
            this._baseDataSourceConstructor(this._adapter);
        },
        {
            nuke: function () {
                this._adapter.nuke();
            },

            remove: function (key) {
                this._adapter.removeInternal(key);
            },

            add: function (obj) {
                this._adapter.insertAtEnd(null, obj);
            },

            get: function (key) {
                return this._adapter.get(key);
            },

            put: function (data) {
                return this._adapter.put(data);
            },

            setDate: function(date) {
                return this._adapter.setDate(date);
            },

            refresh: function () {
                return this._adapter.refresh();
            }
        }
        );

    WinJS.Namespace.define("FolioFixer.DataSources", {
        PortfolioDataSource: PortfolioLocalDataSource,
        AssetDataSource: AssetDataSource
    });

    
})();